home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (C) 1990 Free Software Foundation, Inc.
-
- This file is part of Oleo, the GNU Spreadsheet.
-
- Oleo is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
- any later version.
-
- Oleo is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Oleo; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- #include "funcdef.h"
-
- #include <stdio.h>
-
- #define obstack_chunk_alloc ck_malloc
- #define obstack_chunk_free free
- #include "obstack.h"
-
- #include "sysdef.h"
-
- #include "global.h"
- #include "cell.h"
-
- /* How many rows/columns to allocate to either side of an allocated cell */
- #ifdef __TURBOC__
- #define ROW_BUFF 0
- #define COL_BUFF 0
- #else
- #define ROW_BUFF 4
- #define COL_BUFF 2
- #endif
-
- #ifdef REVERSE
- #define MIN_R MIN_COL
- #define MAX_R MAX_COL
- #define MIN_C MIN_ROW
- #define MAX_C MAX_ROW
- #else
- #define MIN_R MIN_ROW
- #define MAX_R MAX_ROW
- #define MIN_C MIN_COL
- #define MAX_C MAX_COL
- #endif
-
- extern void byte_free EXT1(unsigned char *);
- extern void flush_variables EXT0();
-
- typedef struct col COL;
- typedef struct row ROW;
-
- /* Cells are stored in a two-dimentional sparse array. This allows us to have
- a fair-sized namespace (255x255 or 65535x65535) without using incredible
- amounts of memory. (64Kx64K cells would use up 120GIG of memory just for
- the cell structures. . .)
-
- Note that the sparse arrays are not currently garbage collected, although
- flush_everything will free all the row and column structures, as well
- as everything in them. . .
- */
- struct row {
- ROW *row_next;
- CELLREF row_low;
- CELLREF row_high;
- COL *cols[1];
- };
-
- static ROW *the_rows;
-
- struct col {
- struct col *col_next;
- CELLREF col_low;
- CELLREF col_high;
- CELL cells[1];
- };
-
- static ROW *make_row_at EXT3(CELLREF, ROW *, ROW **);
- static ROW *row_alloc EXT3N(CELLREF, CELLREF, ROW *);
-
- static void out_of_date EXT0();
-
- static COL *col_alloc EXT3N(CELLREF, CELLREF, COL *);
-
- extern CELL *my_cell;
- extern CELLREF cur_row,cur_col;
-
- #define row_free(x) out_of_date(),free(x)
- #define col_free(x) \
- do{\
- out_of_date();\
- if( my_cell \
- && my_cell>= &(x->cells[0]) \
- && my_cell<=&(x->cells[x->col_high-x->col_low])) \
- my_cell=find_cell(cur_row,cur_col); \
- free(x); \
- } while (0)
-
- #define MIN(x,y) ((x)<(y) ? (x) : (y))
- #define MAX(x,y) ((x)>=(y) ? (x) : (y))
-
- /* find_cell() returns a pointer to a cell at 'row', 'col'. If there is no
- cell currently allocated there, it returns 0. */
-
- CELL *
- find_cell FUN2(CELLREF, row, CELLREF, col)
- {
- ROW *row_ptr;
- COL *col_ptr;
-
- #ifdef REVERSE
- CELLREF tmp;
-
- tmp=row;
- row=col;
- col=tmp;
- #endif
- #ifdef TEST
- if(row<MIN_R || row>MAX_R || col<MIN_C || col>MAX_C)
- panic("%u,%u out of range in find_cell",row,col);
- #endif
-
- for(row_ptr=the_rows;row_ptr;row_ptr=row_ptr->row_next) {
- if(row_ptr->row_low<=row && row_ptr->row_high>=row)
- break;
- if(row_ptr->row_low>row) {
- row_ptr=0;
- break;
- }
- }
- if(!row_ptr)
- return 0;
-
- for(col_ptr=row_ptr->cols[row-row_ptr->row_low];col_ptr;col_ptr=col_ptr->col_next) {
- if(col_ptr->col_low<=col && col_ptr->col_high>=col)
- break;
- if(col_ptr->col_low>col) {
- col_ptr=0;
- break;
- }
- }
-
- if(!col_ptr)
- return 0;
- return &(col_ptr->cells[col-col_ptr->col_low]);
- }
-
- /* find_or_make_cell() is like find_cell(), except that if there is no cell
- allocated there, it allocates one. It should never return zero. */
-
- CELL *
- find_or_make_cell FUN2(CELLREF, row, CELLREF, col)
- {
- ROW *row_ptr;
-
- COL *col_ptr;
- COL **col_prev;
- #ifdef REVERSE
- CELLREF tmp;
-
- tmp=row;
- row=col;
- col=tmp;
- #endif
- #ifdef TEST
- if(row<MIN_R || row>MAX_R || col<MIN_C || col>MAX_C)
- panic("%u,%u out of range in find_or_make_cell",row,col);
- #endif
-
- row_ptr=make_row_at(row,the_rows,&the_rows);
-
- /* cdr down the list of cols until we find the one we want */
- col_prev= &(row_ptr->cols[row-row_ptr->row_low]);
- for(col_ptr= *col_prev;col_ptr;col_ptr= *col_prev) {
-
- /* Have we found the right place? */
- if(col_ptr->col_low<=col && col_ptr->col_high>=col)
- break;
-
- if(col_ptr->col_low>col) {
- /* We've cdr'd past the place in the list where
- the column goes */
- if(col_ptr->col_low>col+COL_BUFF+1) {
- /* There isn't anything nearby. */
- col_ptr=0;
- } else {
- int new_low;
- int old_num_cols;
- COL *new_col;
-
- /* Grow the column structure down a bit so
- that this column will fit in it */
- new_low=col-COL_BUFF;
- if(new_low<MIN_C)
- new_low=MIN_C;
- old_num_cols=1+col_ptr->col_high-col_ptr->col_low;
-
- new_col=col_alloc(new_low,
- col_ptr->col_high,
- col_ptr->col_next,
-
- old_num_cols,
- col_ptr->col_low,
- &(col_ptr->cells[0]),
-
- 0);
-
- *col_prev=new_col;
-
- col_free(col_ptr);
- col_ptr=new_col;
- }
- break;
- }
- if(col_ptr->col_high+COL_BUFF+1>=col) {
- /* The column doesn't fit in the current
- col structure, but it can be made to by stretching
- or by merging two col structures.
- So do it. */
- if(col_ptr->col_next && col_ptr->col_next->col_low<=col+COL_BUFF+1) {
- /* This col structure is close enough to the
- next one that they should just be merged */
- COL *the_next;
- COL *new_col;
-
- the_next=col_ptr->col_next;
- if(the_next->col_low<=col) {
- col_ptr=the_next;
- break;
- }
-
- new_col=col_alloc(col_ptr->col_low,
- the_next->col_high,
- the_next->col_next,
-
- 1+col_ptr->col_high-col_ptr->col_low,
- col_ptr->col_low,
- &(col_ptr->cells[0]),
-
- 1+the_next->col_high-the_next->col_low,
- the_next->col_low,
- &(the_next->cells[0]),
-
- 0);
-
- *col_prev=new_col;
-
- col_free(col_ptr);
- col_free(the_next);
- col_ptr=new_col;
- } else {
- int new_high;
- COL *new_col;
-
- /* Stretch the col structure a little */
- if(col>MAX_C-COL_BUFF)
- new_high=MAX_C;
- else
- new_high=col+COL_BUFF;
-
- new_col=col_alloc(col_ptr->col_low,
- new_high,
- col_ptr->col_next,
-
- 1+col_ptr->col_high-col_ptr->col_low,
- col_ptr->col_low,
- &(col_ptr->cells[0]),
-
- 0);
-
- *col_prev=new_col;
-
- col_free(col_ptr);
- col_ptr=new_col;
- }
- break;
- }
- col_prev= &(col_ptr->col_next);
- }
- if(!col_ptr) {
- /* We couldn't find any columns. Create some */
-
- if(col<MIN_C+COL_BUFF)
- col_ptr=col_alloc(MIN_C,
- col+COL_BUFF,
- *col_prev,
-
- 0);
- else if(col>MAX_C-COL_BUFF)
- col_ptr=col_alloc(col-COL_BUFF,
- MAX_C,
- *col_prev,
-
- 0);
- else
- col_ptr=col_alloc(col-COL_BUFF,
- col+COL_BUFF,
- *col_prev,
-
- 0);
- *col_prev= col_ptr;
- }
-
- return &(col_ptr->cells[col-col_ptr->col_low]);
- }
-
- /* These two return the maximum row/column allocated at the given column/row
- Because of the way the sparse array works, one of them will return the
- same value for all possible inputs.
- */
-
- CELLREF
- #ifdef REVERSE
- max_col FUN1(CELLREF, row)
- #else
- max_row FUN1(CELLREF, col)
- #endif
- {
- ROW *rr;
-
- if(!the_rows)
- return MIN_R;
- for(rr=the_rows;rr->row_next;rr=rr->row_next)
- ;
- return rr->row_high;
- }
-
- CELLREF
- #ifdef REVERSE
- max_row FUN1(CELLREF, row)
- #else
- max_col FUN1(CELLREF, row)
- #endif
- {
- ROW *rr;
- COL *cc;
-
- if(!the_rows)
- return MIN_C;
- for(rr=the_rows;rr;rr=rr->row_next)
- if(rr->row_low<=row && rr->row_high>=row)
- break;
- if(!rr)
- return MIN_C;
- for(cc=rr->cols[row-rr->row_low];cc && cc->col_next;cc=cc->col_next)
- ;
- if(!cc)
- return MIN_C;
- return cc->col_high;
- }
-
- /* These two return the highest row/column allocated anywhere in the
- spreadsheet. One of them is easy to find, but the other requires scanning
- the entire sparse array
- */
- CELLREF
- #ifdef REVERSE
- highest_col FUN0()
- #else
- highest_row FUN0()
- #endif
- {
- ROW *rr;
-
- if(!the_rows)
- return MIN_R;
- for(rr=the_rows;rr->row_next;rr=rr->row_next)
- ;
- return rr->row_high;
- }
-
- CELLREF
- #ifdef REVERSE
- highest_row FUN0()
- #else
- highest_col FUN0()
- #endif
- {
- CELLREF ret;
- int n;
- ROW *rr;
- COL *cc;
-
- if(!the_rows)
- return MIN_C;
- ret=MIN_C;
- for(rr=the_rows;rr;rr=rr->row_next) {
- for(n=0;n<rr->row_high-rr->row_low;n++) {
- for(cc=rr->cols[n];cc && cc->col_next;cc=cc->col_next)
- ;
- if(cc && cc->col_high>ret)
- ret=cc->col_high;
- }
- }
- return ret;
- }
-
- /* This is a complicated function. It takes four or more args.
- The first three are
- The lowest row value we should create
- The highest row value we should create
- the next row structure in the list
- and groups of
- how many col ptrs are in this group (N_IN)
- what row value this group starts at (ROW_IN)
- and a pointer to an array of struct col *s (COL_IN)
- The groups end with a (partial) group whose N_IN is zero
- */
-
- static ROW *
- row_alloc FUN3N(CELLREF,r_low,CELLREF,r_high,ROW *,r_next)
- {
- ROW *ret;
- int num_rows;
- int our_row;
- COL **colp;
- int n_in;
- int row_in;
- COL **col_in;
- va_list args;
-
- #ifdef TEST
- if(r_next && r_next->row_low==r_high+1)
- error_msg("Warning: Consecutive rows allocated");
- #endif
- out_of_date();
- num_rows=1+r_high-r_low;
- ret=(ROW *)ck_malloc(sizeof(ROW)+(num_rows-1)*sizeof(ROW *));
- ret->row_low=r_low;
- ret->row_high=r_high;
- ret->row_next=r_next;
-
- /* printf("row_alloc(%d,%d,%d,",r_low,r_high,r_next); */
- var_start(args,r_next);
- our_row=r_low;
- colp= &(ret->cols[0]);
- while(n_in=va_arg(args,int)) {
- row_in=va_arg(args,/* CELLREF */ int);
- while(our_row<row_in) {
- our_row++;
- *colp++=0;
- }
- col_in=(COL **)va_arg(args,COL **);
- /* printf(" %d,%d,%d,",n_in,row_in,col_in); */
- while(n_in--) {
- our_row++;
- *colp++ = *col_in++;
- }
- }
- while(our_row++<=r_high)
- *colp++=0;
- /* printf("0)\n"); */
- va_end(args);
-
- return ret;
- }
-
- /* This is like row_alloc(), but it allocates columns instead */
- static COL *
- col_alloc FUN3N(CELLREF,c_low,CELLREF,c_high,COL *,c_next)
- {
- COL *ret;
- int num_cols;
- int our_col;
- CELL *cellp;
- int n_in;
- int col_in;
- CELL *cell_in;
- va_list args;
-
- #ifdef TEST
- if(c_next && c_next->col_low==c_high+1)
- error_msg("Warning: Consecutive cols allocated");
- #endif
- out_of_date();
- num_cols=1+c_high-c_low;
- ret=(COL *)ck_malloc(sizeof(COL)+(num_cols-1)*sizeof(CELL));
- ret->col_low=c_low;
- ret->col_high=c_high;
- ret->col_next=c_next;
-
- /* printf("col_alloc(%d,%d,%d,",c_low,c_high,c_next); */
- var_start(args,c_next);
- our_col=c_low;
- cellp= &(ret->cells[0]);
- while(n_in=va_arg(args,int)) {
- col_in=va_arg(args,/* CELLREF */ int);
- while(our_col<col_in) {
- our_col++;
- /* cellp->cell_string=0; */
- cellp->cell_refs_from=0;
- cellp->cell_refs_to=0;
- cellp->cell_cycle=0;
- cellp->cell_formula=0;
- cellp++->cell_flags=0;
- }
- cell_in=(CELL *)va_arg(args,CELL *);
- /* printf(" %d,%d,%d,",n_in,col_in,cell_in); */
- bcopy(cell_in,cellp,n_in*sizeof(CELL));
- our_col+=n_in;
- cellp+=n_in;
- }
-
- /* printf(" 0)\n"); */
- while(our_col++<=c_high) {
- /* cellp->cell_string=0; */
- cellp->cell_refs_from=0;
- cellp->cell_refs_to=0;
- cellp->cell_cycle=0;
- cellp->cell_formula=0;
- cellp++->cell_flags=0;
- }
-
- return ret;
- }
-
- /* This function returns a pointer to a row structure that has a row defined
- at ROW. PREVP points to a row structure somewhere before ROW. PREVPP
- points to the place where the pointer to PREVP is stored (for use if
- we have to realloc(PREVP,...) */
- static ROW *
- make_row_at FUN3(CELLREF,row,ROW *,prevp,ROW **,prevpp)
- {
- ROW *postp;
- ROW *new_row;
- CELLREF rowhi,rowlo;
-
- while(prevp && prevp->row_next && prevp->row_next->row_low < row) {
- prevpp = &(prevp->row_next);
- prevp=prevp->row_next;
- }
-
- if(prevp && prevp->row_low<=row && prevp->row_high>=row)
- return prevp;
-
- rowhi= (row>MAX_R-ROW_BUFF) ? MAX_R : row+ROW_BUFF;
- rowlo= (row<MIN_R+ROW_BUFF) ? MIN_R : row-ROW_BUFF;
-
- if( !prevp
- || ( prevp->row_high < rowlo-1
- && (!prevp->row_next || prevp->row_next->row_low-1>rowhi))) {
-
- /* There's no row structure there. Therefore, create one */
- new_row=row_alloc(rowlo,
- rowhi,
- prevp ? prevp->row_next : *prevpp,
-
- 0);
- if(prevp)
- prevp->row_next=new_row;
- else
- *prevpp=new_row;
- return new_row;
- }
-
- if(prevp->row_high+1>=rowlo) {
- if(prevp->row_next && prevp->row_next->row_low-1<=rowhi) {
- /* This row is (nearly) midway between two row
- structures. Merge them */
- postp=prevp->row_next;
- new_row=row_alloc(prevp->row_low,
- postp->row_high,
- postp->row_next,
-
- 1+prevp->row_high-prevp->row_low,
- prevp->row_low,
- &(prevp->cols[0]),
-
- 1+postp->row_high-postp->row_low,
- postp->row_low,
- &(postp->cols[0]),
-
- 0);
-
- *prevpp=new_row;
-
- row_free(prevp);
- row_free(postp);
- return new_row;
- } else if(prevp->row_low>rowlo) {
- /* The first row struct must be stretched up a bit */
- new_row=row_alloc(rowlo,prevp->row_high,prevp->row_next,
- 1+prevp->row_high-prevp->row_low,
- prevp->row_low,
- &(prevp->cols[0]),
-
- 0);
- *prevpp=new_row;
- row_free(prevp);
- return new_row;
- } else {
- /* This row is just past the end of a row
- structure. Stretch the structure a bit so
- that it'll fit */
- new_row=row_alloc(prevp->row_low,
- rowhi,
- prevp->row_next,
-
- 1+prevp->row_high-prevp->row_low,
- prevp->row_low,
- &(prevp->cols[0]),
-
- 0);
-
- *prevpp=new_row;
-
- row_free(prevp);
- return new_row;
- }
- }
-
- if(prevp->row_next && prevp->row_next->row_low-1<=rowhi) {
- /* Grow the old row_structure down just a
- little so that this one will fit in it */
- prevpp= &(prevp->row_next);
- postp=prevp->row_next;
-
- new_row=row_alloc(rowlo,
- postp->row_high,
- postp->row_next,
-
- 1+postp->row_high-postp->row_low,
- postp->row_low,
- &(postp->cols[0]),
-
- 0);
-
- *prevpp=new_row;
-
- row_free(postp);
- return new_row;
- }
- #ifdef TEST
- if(prevp->row_low<row || prevp->row_high>row)
- panic("Make row at: %u not in %u %u",row,prevp->row_low,prevp->row_high);
- #endif
- return prevp;
- }
-
-
- /* The next group of routines deal with finding groups of cells in the
- spreadsheet. They should be smart enough to deal with the spreadsheet
- changing under them. */
- struct find {
- CELLREF lr,lc,hr,hc;
- CELLREF cr,cc;
- int flags;
-
- ROW *nowrow;
- COL **colpptr;
- COL *nowcol;
- int colsleft;
- CELL *nowcell;
- int cellsleft;
- struct find *next;
- };
- static struct find *f;
- static struct obstack find_stack;
-
- void
- init_cells FUN0()
- {
- obstack_begin(&find_stack,sizeof(struct find)*15);
- }
-
- /* This sets things up so that the next gzillion calls to next_cell_in_range()
- will return one of the cells in the region delineated by RNG. If
- This will skip over any gaps in the region, but will return zeroed cells
- in the region. If there are no cells in the region, the first call to
- next_cell_in_range() will return 0
- */
- void
- find_cells_in_range FUN1(struct rng *,rng)
- {
- struct find *oldf;
- #ifdef REVERSE
- #define LR rng->lc
- #define LC rng->lr
- #define HR rng->hc
- #define HC rng->hr
- #else
- #define LR rng->lr
- #define LC rng->lc
- #define HR rng->hr
- #define HC rng->hc
- #endif
- #ifdef TEST
- if( LR<MIN_R || LR>MAX_R || HR<MIN_R || HR>MAX_R
- || LC<MIN_C || LC>MAX_C || HC<MIN_C || HC>MAX_C)
- panic("find_cells_in_range(%u:%u %u:%u) out of range",LR,HR,LC,HC);
- #endif
-
- oldf=f;
- f=obstack_alloc(&find_stack,sizeof(struct find));
- bzero(f,sizeof(struct find));
- f->next=oldf;
- f->lr=LR;
- f->hr=HR;
- f->lc=LC;
- f->hc=HC;
- for(f->nowrow=the_rows;f->nowrow;f->nowrow=f->nowrow->row_next)
- if(f->nowrow->row_high>=f->lr)
- break;
- while(f->nowrow && f->nowrow->row_low<=f->hr) {
- f->cr=f->lr<=f->nowrow->row_low ? f->nowrow->row_low : f->lr;
- f->colpptr= &(f->nowrow->cols[f->cr-f->nowrow->row_low]);
- f->colsleft=1+MIN(f->hr,f->nowrow->row_high)-f->cr;
- while(f->colsleft) {
- --(f->colsleft);
- f->nowcol= *(f->colpptr)++;
- while(f->nowcol && f->nowcol->col_high<f->lc)
- f->nowcol=f->nowcol->col_next;
- if(f->nowcol && f->nowcol->col_low<=f->hc) {
- /* Found something */
- f->cc=(f->lc<f->nowcol->col_low) ? f->nowcol->col_low : f->lc;
- f->nowcell= &(f->nowcol->cells[f->cc-f->nowcol->col_low]);
- f->cellsleft=1+MIN(f->hc,f->nowcol->col_high)-f->cc;
- --(f->cc);
- #ifdef TEST
- if(f->cellsleft<1)
- panic("Cellsleft %d<1",f->cellsleft);
- #endif
- f->flags=0;
- return;
- }
- (f->cr)++;
- }
- f->nowrow=f->nowrow->row_next;
- }
- /* If we get here, we searched through the entire range, and didn't
- find *any* cells. We lose */
- f->flags=1;
- }
-
- /* This is like find_cells_in_range, except that if the cells don't exist,
- it makes them! */
- void
- make_cells_in_range FUN1(struct rng *,rng)
- {
- ROW **prevpp;
- ROW *myrow;
- int n;
- COL **colpp;
- COL *mycol;
- struct find *oldf;
- CELLREF lr,hr,lc,hc;
-
- #ifdef TEST
- if( LR<MIN_R || LR>MAX_R || HR<MIN_R || HR>MAX_R
- || LC<MIN_C || LC>MAX_C || HC<MIN_C || HC>MAX_C)
- panic("make_cells_in_range(%u:%u %u:%u) out of range",LR,HR,LC,HC);
- #endif
- oldf=f;
- f=obstack_alloc(&find_stack,sizeof(struct find));
- bzero(f,sizeof(struct find));
- f->next=oldf;
- f->lr=LR;
- f->lc=LC;
- f->hr=HR;
- f->hc=HC;
-
- f->colsleft=f->hr-f->lr;
- f->cr=f->lr;
- f->cellsleft=1+f->hc-f->lc;
- f->cc=f->lc-1;
-
- lr= f->lr<=MIN_R+ROW_BUFF ? MIN_R : f->lr-ROW_BUFF;
- hr= f->hr>=MAX_R-ROW_BUFF ? MAX_R : f->hr+ROW_BUFF;
-
- if(!the_rows) {
- myrow=the_rows=row_alloc(lr,hr,0,0);
- } else {
- myrow=the_rows;
- prevpp= &the_rows;
- while(myrow && myrow->row_high<lr-1) {
- prevpp= &(myrow->row_next);
- myrow=myrow->row_next;
- }
- if(!myrow || myrow->row_low-1>hr)
- myrow= *prevpp=row_alloc(lr,hr,myrow,0);
- else if(myrow->row_high<hr) {
- ROW *t1,*t2;
-
- while(myrow->row_high<hr && myrow->row_next && myrow->row_next->row_low<=hr+1) {
- t1=myrow;
- t2=myrow->row_next;
- myrow= *prevpp=row_alloc(t1->row_low,t2->row_high,t2->row_next,
- 1+t1->row_high-t1->row_low,t1->row_low,&(t1->cols[0]),
- 1+t2->row_high-t2->row_low,t2->row_low,&(t2->cols[0]),
- 0);
- row_free(t1);
- row_free(t2);
- }
- if(myrow->row_high<hr) {
- t1=myrow;
- myrow= *prevpp=row_alloc(t1->row_low,hr,t1->row_next,
- 1+t1->row_high-t1->row_low,t1->row_low,&(t1->cols[0]),
- 0);
- row_free(t1);
- }
- } else if(myrow->row_low>lr) {
- ROW *t1;
-
- t1=myrow;
- myrow= *prevpp=row_alloc(lr,t1->row_high,t1->row_next,
- 1+t1->row_high-t1->row_low,t1->row_low,&(t1->cols[0]),
- 0);
- row_free(t1);
- }
- }
- #ifdef TEST
- if(myrow->row_low>f->lr || myrow->row_high<f->hr)
- panic("Myrow isn't big enough");
- #endif
- lc= f->lc<=MIN_C+COL_BUFF ? MIN_C : f->lc-COL_BUFF;
- hc= f->hc>=MAX_C-COL_BUFF ? MAX_C : f->hc+COL_BUFF;
-
- for(n=lr-myrow->row_low;n<=hr-myrow->row_low;n++) {
-
- colpp= &(myrow->cols[n]);
- if(!*colpp) {
- *colpp=col_alloc(lc,hc,0,0);
- } else {
- for(mycol= *colpp;mycol;colpp= &(mycol->col_next),mycol= *colpp)
- if(mycol->col_high+1>=lc)
- break;
- if(!mycol || mycol->col_low>hc+1)
- *colpp=col_alloc(lc,hc,mycol,0);
- else if(mycol->col_low<=lc && mycol->col_high>=hc)
- ; /* Easy */
- else /* if(mycol->col_low>lc && mycol->col_high>hc) {
- COL *t1;
-
- t1=mycol;
- mycol= *colpp=col_alloc(lc,t1->col_high,t1->col_next,
- 1+t1->col_high-t1->col_low,t1->col_low,&(t1->cells[0]),
- 0);
- col_free(t1);
- } else */ {
- COL *t1,*t2;
-
- while(mycol->col_high<=hc+1
- && mycol->col_next
- && mycol->col_next->col_low<=hc+1) {
- t1=mycol;
- t2=mycol->col_next;
- mycol= *colpp=col_alloc(MIN(lc,t1->col_low),t2->col_high,t2->col_next,
- 1+t1->col_high-t1->col_low,t1->col_low,&(t1->cells[0]),
- 1+t2->col_high-t2->col_low,t2->col_low,&(t2->cells[0]),
- 0);
- col_free(t1);
- col_free(t2);
- }
- if(mycol->col_low>lc || mycol->col_high<hc) {
- t1=mycol;
- mycol= *colpp=col_alloc(MIN(lc,t1->col_low),MAX(hc,mycol->col_high),t1->col_next,
- 1+t1->col_high-t1->col_low,t1->col_low,&(t1->cells[0]),
- 0);
- col_free(t1);
- }
- }
- }
- }
- f->nowrow=myrow;
- f->colpptr= &(f->nowrow->cols[f->lr-f->nowrow->row_low]);
- f->nowcol= *(f->colpptr)++;
- while(f->nowcol->col_high<f->lc)
- f->nowcol=f->nowcol->col_next;
- f->nowcell= &(f->nowcol->cells[f->lc-f->nowcol->col_low]);
-
- #ifdef TEST
- if(f->cellsleft<1)
- panic("Cellsleft %d<1",f->cellsleft);
- #endif
- f->flags=0;
- }
-
- /* Return the next cell in the range previously selected by
- find_cells_in_range() or make_cells_in_range() If row_alloc(),
- row_free(), col_alloc(), or col_free() has been called, re-sync to the
- current state of the spreadsheet. */
-
- CELL *
- next_cell_in_range FUN0()
- {
- struct find *oldf;
-
- #ifdef TEST
- if(!f)
- panic("No 'f' in next_cells_in_range!");
- #endif
- if(f->flags) {
- if(f->flags&1) {
- done:
- oldf=f->next;
- (void)obstack_free(&find_stack,f);
- f=oldf;
- return 0;
- }
- f->flags=0;
- if(f->cr==f->hr && f->cc==f->hc)
- goto done;
- if(f->cc==f->hc) { /* JF was f->cc=f->hc */
- f->cc=f->lc-1;
- (f->cr)++;
- }
- for(f->nowrow=the_rows;f->nowrow;f->nowrow=f->nowrow->row_next)
- if(f->nowrow->row_high>=f->cr)
- break;
- if(!f->nowrow)
- goto done;
- f->colpptr= &(f->nowrow->cols[f->cr-f->nowrow->row_low]);
- f->colsleft= 1+MIN(f->hr,f->nowrow->row_high)-f->cr;
- if(f->colsleft) {
- --(f->colsleft);
- f->nowcol= *(f->colpptr)++;
- while(f->nowcol && f->nowcol->col_high<f->cc)
- f->nowcol=f->nowcol->col_next;
- if(f->nowcol && f->nowcol->col_low<=f->cc) {
- (f->cc)++;
- f->nowcell= &(f->nowcol->cells[f->cc-f->nowcol->col_low]);
- f->cellsleft=1+MIN(f->hc,f->nowcol->col_high)-f->cc;
- --(f->cc);
- }
- }
- }
-
- if(f->cellsleft) {
- #ifdef TEST
- if(f->cellsleft<1)
- panic("Cellsleft %d<1",f->cellsleft);
- #endif
- --(f->cellsleft);
- (f->cc)++;
- return (f->nowcell)++;
- }
-
- /* There are no more cells in the current COL structure. If there
- might be cells in the next one, try there */
- if(f->nowcol->col_high<f->hc) {
- f->nowcol=f->nowcol->col_next;
- if(f->nowcol && f->nowcol->col_low<=f->hc) {
- f->nowcell= &(f->nowcol->cells[0]);
- f->cellsleft=MIN(f->hc,f->nowcol->col_high)-f->nowcol->col_low;
- f->cc=f->nowcol->col_low;
- #ifdef TEST
- if(f->cellsleft<0)
- panic("Cellsleft %d<1",f->cellsleft);
- #endif
- return (f->nowcell)++;
- }
- }
- try_cols:
- /* See if there are any more COLS in the current row structure
- If there are, scan through them for useful cells. */
- while(f->colsleft) {
- --(f->colsleft);
- (f->cr)++;
- f->nowcol= *(f->colpptr)++;
- while(f->nowcol && f->nowcol->col_high<f->lc)
- f->nowcol=f->nowcol->col_next;
- if(f->nowcol && f->nowcol->col_low<=f->hc) { /* Found something */
- f->cc=f->lc<f->nowcol->col_low ? f->nowcol->col_low : f->lc;
- f->nowcell= &(f->nowcol->cells[f->cc-f->nowcol->col_low]);
- f->cellsleft=MIN(f->hc,f->nowcol->col_high)-f->cc;
- #ifdef TEST
- if(f->cellsleft<0)
- panic("Cellsleft %d<1",f->cellsleft);
- #endif
- return (f->nowcell)++;
- }
- }
-
- /* Have we run out of rows? */
- if(f->nowrow->row_high>=f->hr)
- goto done;
-
- /* Check in the next ROW structure. There's nothing in this one */
- f->nowrow=f->nowrow->row_next;
- if(!f->nowrow || f->nowrow->row_low>f->hr)
- goto done;
-
- f->colpptr= &(f->nowrow->cols[0]);
- f->colsleft= 1 + MIN(f->hr,f->nowrow->row_high)-f->nowrow->row_low;
- f->cr=f->nowrow->row_low-1;
- goto try_cols;
- }
-
- /* This is a wrapper to next_cell_in_range that also returns (through
- PUTROW and PUTCOL information about where the cell actually *is*
- */
- CELL *
- next_row_col_in_range FUN2(CELLREF *,putrow, CELLREF *,putcol)
- {
- CELL *nxt;
-
- nxt=next_cell_in_range();
- if(nxt) {
- #ifdef REVERSE
- *putcol=f->cr;
- *putrow=f->cc;
- #else
- *putrow=f->cr;
- *putcol=f->cc;
- #endif
- } else {
- *putrow=MIN_R-1;
- *putcol=MIN_C-1;
- }
- return nxt;
- }
-
- /* This should be called if a function called {find,make}_cells_in_range()
- and wants to stop calling next_{cell,row_col}_in_range() before they
- encounter the end of the range */
- void
- no_more_cells FUN0()
- {
- struct find *oldf;
-
- oldf=f->next;
- (void)obstack_free(&find_stack,f);
- f=oldf;
- }
-
- /* This is called by row_alloc and col_alloc to tell
- next_{cell,row_col}_in_range() that some part of the spreadsheet has
- changed.
- */
- static void
- out_of_date FUN0()
- {
- struct find *tmpf;
-
- for(tmpf=f;tmpf;tmpf=tmpf->next)
- tmpf->flags|=2;
- }
-
- /* this is your basic trash-the-world function. */
- void
- flush_everything FUN0()
- {
- ROW *rp,*rnxt;
- COL *cp,*cnxt;
- CELL *sp;
-
- int nrn;
- #ifndef SPLIT_REFS
- extern void flush_refs();
- #endif
-
- for(rp= the_rows;rp;rp=rnxt) {
- rnxt=rp->row_next;
- for(nrn=rp->row_low;nrn<=rp->row_high;nrn++) {
- for(cp= rp->cols[nrn-rp->row_low];cp;cp= cnxt) {
- cnxt=cp->col_next;
- for(sp= &(cp->cells[0]);sp<=&(cp->cells[cp->col_high-cp->col_low]);sp++) {
- #ifdef SPLIT_REFS
- if(sp->cell_refs_from)
- free(sp->cell_refs_from);
- if(sp->cell_refs_to)
- free(sp->cell_refs_to);
- #endif
- if(sp->cell_formula)
- byte_free(sp->cell_formula);
- if(GET_TYP(sp)==TYP_STR)
- free(sp->cell_str);
- }
- free(cp);
- }
- }
- free(rp);
- the_rows=0;
- }
- flush_variables();
- #ifndef SPLIT_REFS
- flush_refs();
- #endif
- }
-
- #ifdef TEST
- extern char *bname[];
- extern char *dbg_print_formula();
- extern char *dbg_print_ref_fm();
- extern char *dbg_print_ref_to();
-
- extern char print_buf[];
-
- extern char *bname[];
-
- void dbg_print_cell();
-
- /* These debugging functions store useful text about the sparse array
- in the buffer pointed to by BUF. If it isn't big enough, you lose. */
-
- void
- dbg_print_rows FUN0()
- {
- ROW *row_ptr;
- CELLREF n,maxx;
- char *buf;
-
- for(row_ptr=the_rows;row_ptr;row_ptr=row_ptr->row_next) {
- text_line("Row %p: next %p, low %u, high %u",
- row_ptr,row_ptr->row_next,row_ptr->row_low,
- row_ptr->row_high);
- maxx=row_ptr->row_high-row_ptr->row_low;
- for(buf=print_buf,n=0;n<=maxx;n++) {
- (void)sprintf(buf," %p",row_ptr->cols[n]);
- if(n%8==7) {
- text_line(print_buf);
- buf=print_buf;
- } else
- buf+=strlen(buf);
- }
- if(n%8)
- text_line(print_buf);
- }
- }
-
- void
- dbg_print_cols FUN1(CELLREF, row)
- {
- ROW *row_ptr;
- COL *col_ptr;
- CELLREF maxc,nc;
- CELL *cp;
- char *buf;
-
- for(row_ptr=the_rows;row_ptr;row_ptr=row_ptr->row_next)
- if(row_ptr->row_low<=row && row_ptr->row_high>=row)
- break;
- if(!row_ptr)
- return;
-
- for(col_ptr=row_ptr->cols[row-row_ptr->row_low];col_ptr;col_ptr=col_ptr->col_next) {
- text_line("Col %p: Next %p, low %u, high %u",
- col_ptr,col_ptr->col_next,col_ptr->col_low,
- col_ptr->col_high);
- maxc=col_ptr->col_high-col_ptr->col_low;
-
- for(cp= &(col_ptr->cells[0]),buf=print_buf,nc=0;nc<=maxc;nc++,cp++) {
- switch(GET_TYP(cp)) {
- case 0:
- (void)strcpy(buf," .");
- break;
- case TYP_FLT:
- (void)sprintf(buf," %8gf",cp->cell_flt);
- break;
- case TYP_INT:
- (void)sprintf(buf," %8ldi",cp->cell_int);
- break;
- case TYP_ERR:
- (void)sprintf(buf," %8.8se",ename[cp->cell_err]);
- break;
- case TYP_BOL:
- (void)sprintf(buf," %8sb",bname[cp->cell_bol]);
- break;
- case TYP_STR:
- (void)sprintf(buf," %8.8ss",cp->cell_str);
- break;
- default:
- (void)sprintf(buf," %8dU",GET_TYP(cp));
- break;
- }
- if(nc%8==7) {
- text_line(print_buf);
- buf=print_buf;
- } else
- buf+=strlen(buf);
- }
- if(nc%8)
- text_line(print_buf);
- }
- }
-
- void
- dbg_print_array FUN0()
- {
- int maxx;
- int nr;
- int maxc;
- int nc;
- COL *col_ptr;
- CELL *cp;
- ROW * row_ptr;
- char *buf;
-
- for(row_ptr=the_rows;row_ptr;row_ptr=row_ptr->row_next) {
- text_line("Row %p: next %p, low %u, high %u",
- row_ptr,row_ptr->row_next,row_ptr->row_low,
- row_ptr->row_high);
- maxx=row_ptr->row_high-row_ptr->row_low;
- for(nr=0,buf=print_buf;nr<=maxx;nr++) {
- (void)sprintf(buf," %p",row_ptr->cols[nr]);
- if(nr%8==7) {
- text_line(print_buf);
- buf=print_buf;
- } else
- buf+=strlen(buf);
- }
- if(nr%8)
- text_line(print_buf);
-
- for(nr=0;nr<=maxx;nr++) {
- for(col_ptr=row_ptr->cols[nr];col_ptr;col_ptr=col_ptr->col_next) {
- text_line("Col %p: Next %p, low %u, high %u",
- col_ptr,col_ptr->col_next,col_ptr->col_low,
- col_ptr->col_high);
- maxc=col_ptr->col_high-col_ptr->col_low;
-
- for(cp= &(col_ptr->cells[0]),buf=print_buf,nc=0;nc<=maxc;nc++,cp++) {
- (void)sprintf(buf," %p",cp);
- if(nc%8==7) {
- text_line(print_buf);
- buf=print_buf;
- } else
- buf+=strlen(buf);
- }
- if(nc%8)
- text_line(print_buf);
-
- for(cp= &(col_ptr->cells[0]),nc=0;nc<=maxc;nc++,cp++)
- dbg_print_cell(cp);
- }
- }
- }
- }
-
- void
- dbg_print_cell FUN1(CELL *,cp)
- {
- char *ptr1,*ptr2;
- char tmpbuf[30];
-
- switch(GET_TYP(cp)) {
- case 0:
- ptr1="(null)";
- ptr2="";
- break;
- case TYP_FLT:
- sprintf(tmpbuf,"Float: %.16g",cp->cell_flt);
- ptr1=tmpbuf;
- ptr2="";
- break;
- case TYP_INT:
- sprintf(tmpbuf,"Int: %ld",cp->cell_int);
- ptr1=tmpbuf;
- ptr2="";
- break;
- case TYP_ERR:
- sprintf(tmpbuf,"Error: %d: ",cp->cell_err);
- ptr1=tmpbuf;
- ptr2=ename[cp->cell_err];
- break;
- case TYP_BOL:
- sprintf(tmpbuf,"Bool: %d: ",cp->cell_bol);
- ptr1=tmpbuf;
- ptr2=bname[cp->cell_bol];
- break;
- case TYP_STR:
- sprintf(tmpbuf,"String: %p: ",cp->cell_str);
- ptr1=tmpbuf;
- ptr2=cp->cell_str;
- break;
- default:
- sprintf(tmpbuf,"Unknown: %d",GET_TYP(cp));
- ptr1=tmpbuf;
- ptr2="";
- break;
- }
- text_line("Cell %p: flags %#lx, cycle %u, formula %p, value %s%s",
- cp,cp->cell_flags,cp->cell_cycle,cp->cell_formula,
- ptr1,ptr2);
- dbg_print_formula(cp->cell_formula);
- dbg_print_ref_fm(cp->cell_refs_from);
- dbg_print_ref_to(cp->cell_refs_to);
- }
-
- #endif
-